/*
 * CS 567 GUI - Semester Project - "DecisionLists"
 * Jason Frank
 * Spring 2011
 *
 * This File:  Implements the "Spreadsheet Summary" view.  
 */

package Decision_Lists;

import Decision_Lists.List_Item_Data.ProCon;
import com.thebuzzmedia.imgscalr.Scalr;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import sas.swing.MultiLineLabel;
import sas.swing.plaf.MultiLineLabelUI;
//import sun.swing.table.DefaultTableCellHeaderRenderer;

/*
 */
public class SpreadsheetView extends JPanel implements DL_ViewInterface {

  protected boolean is_center_view = true;
  protected DL_Model model;
  protected final String view_name = "Spreadsheet";
  boolean drawRectOnTop = false;
  //protected int panel_width = 500;
  //protected int panel_height = 500;

  protected int TOP_PADDING  = 20;
  protected int BTM_PADDING  = 8;
  protected int row_height   = 125;
  protected int myScrollPane_PrefHeight = row_height * 5 + 40; // +40 for header
  protected int myScrollPane_MaxHeight  = 1000;

  protected JTable table;
  // ROW INDEXES
  protected int nameColNum;       // = 0;// These will get set in nested class MyTableModel
  protected int mainPicColumnNum; //
  protected int locationPicColNum;
  protected int priceColNum;      
  protected int summaryColNum;    
  protected int prosColNum;   
  protected int consColNum;   

  protected int lastColNum;     // the last column of the table. Needed to fill extra space
  // COLUMN WIDTHS
  protected int mainPicColWidth  = 170;
  protected int locationPicColWidth  = 170;
  protected int nameColWidth     = 125;
  protected int priceColWidth    =  85;
  protected int summaryColWidth  = 130;
  protected int prosColWidth     = 135;
  protected int consColWidth     = 135;
  protected int lastColWidth     = consColWidth;

  protected int wrapWidthPad = 17; // needed to force wrapping
  protected int allColsSumWidth; // not used now?

  protected TableColumn nameCol, mainPicCol, locationPicCol, priceCol, summaryCol, prosCol, consCol;
  protected Vector <TableColumn> tableColsVec = new Vector<TableColumn>();

  Font nameFont =  new Font("Helvetica", Font.BOLD,  16);
  Font plainFont = new Font("Helvetica", Font.PLAIN, 12);

  //JScrollPane scrollPane; // to hold the table
  MyScrollPane scrollPane; // to hold the table
  MyTableModel myTableModel;

  TableColumnModelListener tableColListener;

  ///// END CLASS VARS /////////
  //////////////////////////////


  // Constructor
  public SpreadsheetView(DL_Model model)  {
    this.model = model;           // link the model to this view
    this.model.addObserver(this, this.view_name, is_center_view); // link this view to the model

            // Temporary
            //JButton btn = new JButton("a button");
            //add(btn);

    myTableModel = new MyTableModel();
    table = new JTable(myTableModel); // MyTableModel uses the MVC's model to populate table data
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));
    table.setFillsViewportHeight(true);

    
    nameCol    = table.getColumnModel().getColumn(nameColNum);
    mainPicCol = table.getColumnModel().getColumn(mainPicColumnNum);
    locationPicCol = table.getColumnModel().getColumn(locationPicColNum);
    priceCol   = table.getColumnModel().getColumn(priceColNum);
    summaryCol = table.getColumnModel().getColumn(summaryColNum);
    prosCol    = table.getColumnModel().getColumn(prosColNum);
    consCol    = table.getColumnModel().getColumn(consColNum);

    // Must add these to the vec in same order that they are added to MyTableModel
    tableColsVec.add(nameCol);
    tableColsVec.add(mainPicCol);
    tableColsVec.add(locationPicCol);
    tableColsVec.add(priceCol);
    tableColsVec.add(summaryCol);
    tableColsVec.add(prosCol);
    tableColsVec.add(consCol);
    // Format the headers of all columns
    for(TableColumn tableCol: tableColsVec) {
      tableCol.setHeaderRenderer(new MyHeaderRenderer());
    }

    // Set cell renderers for columns
    mainPicCol.setCellRenderer(new ImageRenderer());
    locationPicCol.setCellRenderer(new ImageRenderer());
    nameCol.setCellRenderer(new JLabelWrapRenderer(nameColWidth - wrapWidthPad,
                                                   nameFont,
                                                   true, /* isHorizCentered */
                                                   true)); /* isVertCentered */
    priceCol.setCellRenderer(new JLabelWrapRenderer(priceColWidth - wrapWidthPad,
                                                   plainFont,
                                                   true, /* isHorizCentered */
                                                   false)); /* isVertCentered - false means top */
    summaryCol.setCellRenderer(new JLabelWrapRenderer(summaryColWidth - wrapWidthPad,
                                                   plainFont,
                                                   false, /* isHorizCentered */
                                                   false)); /* isVertCentered - false means top */
    prosCol.setCellRenderer(new ProConCellRenderer());
    consCol.setCellRenderer(new ProConCellRenderer());

    // Set the ROW HEIGHT big enough
    table.setRowHeight(row_height);


    //Create the scroll pane and add the table to it.
    //scrollPane = new JScrollPane(table);
    scrollPane = new MyScrollPane(table);

    setColumnWidths(); 

    // Set a listener for column size changes so MyScrollPane can set a width big enough
    tableColListener = new TableColumnModelListener() {

      public void columnMarginChanged(ChangeEvent e) {
        //System.out.println("Margin size changed");
        validate();  // get the entire table/scrollpane resized to keep up with resized col
        repaint();
      }
      public void columnAdded(TableColumnModelEvent e) {    }
      public void columnRemoved(TableColumnModelEvent e) {  }
      public void columnMoved(TableColumnModelEvent e) {    }
      public void columnSelectionChanged(ListSelectionEvent e) {  }
    };
    // Now actually add the listener
    table.getColumnModel().addColumnModelListener(tableColListener);
    

    /////////////////////////
    //// Layout /////////////
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

    add(Box.createRigidArea(new Dimension(0, TOP_PADDING)));
    add(Box.createVerticalGlue());
    Box mainHorizBox = Box.createHorizontalBox();
    //Add the scroll pane to this panel.
    //add(scrollPane);
    mainHorizBox.add(Box.createHorizontalGlue());
    mainHorizBox.add(scrollPane);
    mainHorizBox.add(Box.createHorizontalGlue());
    // Must set the size of this box to get the table size I want
    //mainHorizBox.setPreferredSize(new Dimension(1200, 1000));
    mainHorizBox.setPreferredSize(scrollPane.getPreferredSize());
    add(mainHorizBox);
    add(Box.createVerticalGlue());
    add(Box.createRigidArea(new Dimension(0, BTM_PADDING)));


  } ////// end constructor //////

 
  protected void setColumnWidths() {

    // First, must Disable auto resizing
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

    table.getColumnModel().getColumn(nameColNum).setPreferredWidth(nameColWidth);
    table.getColumnModel().getColumn(mainPicColumnNum).setPreferredWidth(mainPicColWidth);
    table.getColumnModel().getColumn(locationPicColNum).setPreferredWidth(locationPicColWidth);

    table.getColumnModel().getColumn(priceColNum).setPreferredWidth(priceColWidth);
    table.getColumnModel().getColumn(summaryColNum).setPreferredWidth(summaryColWidth);

    table.getColumnModel().getColumn(prosColNum).setPreferredWidth(prosColWidth);
    table.getColumnModel().getColumn(consColNum).setPreferredWidth(consColWidth);

  } /// end setColumnWidths() /////////


    //////////////////////////////////////////////////
    /// NESTED CLASS ImageRenderer /////////////////// http://www.java2s.com/Code/Java/Swing-JFC/RenderinganimageinaJTablecollumn.htm
    /*
     * Needed so the JTable will DISPLAY IMAGES correctly (to render them as images)
     */
    class ImageRenderer extends DefaultTableCellRenderer {
       JLabel label = new JLabel();

      //ImageIcon icon = new ImageIcon(getClass().getResource("sample.png"));

      @Override
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                     boolean hasFocus, int row, int column) {

        // Call the super according to "camickr" on stackoverflow: http://stackoverflow.com/questions/3222951/java-put-image-in-jtable-cell
        super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        //lbl.setText((String) value);
        label.setIcon( (Icon)value);

        return label;
      }
    } //////////// end nested class ImageRenderer ///////////////


    ///////////////////////////////////////////////////////
    /// NESTED CLASS JLabelWrapRenderer ///////////////////
    /*
     * 
     */
    class JLabelWrapRenderer extends DefaultTableCellRenderer {

       //JLabel label; // = new JLabel();
       //JLabel renderedLabel = new JLabel();
       MultiLineLabel label = new MultiLineLabel();
       int width;
       int textWidth;
       int sidePad = 10;
       Font font;
       boolean isHorizCentered, isVertCentered;

       // Constructor
       public JLabelWrapRenderer(int width, Font font, boolean isHorizCentered, boolean isVertCentered) {
         this.width = width;
         this.textWidth  = width - 10;
         this.font = font;
         this.isHorizCentered = isHorizCentered;
         this.isVertCentered  = isVertCentered;
       }

      @Override
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                     boolean hasFocus, int row, int column) {

        // Call the super according to "camickr" on stackoverflow: http://stackoverflow.com/questions/3222951/java-put-image-in-jtable-cell
        super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        // <editor-fold defaultstate="collapsed" desc="Controlling line wrap/line width via HTML (can't get ellipses)">
        /*
        if(isHorizCentered) {
        label.setText(String.format("<html><div style=\"width:%dpx; text-align:center; "
        + "\">%s</div><html>",
        textWidth - (2*sidePad) , (String)value));
        }
        else {
        label.setText(String.format("<html><div style=\"width:%dpx; "
        + "\">%s</div><html>",
        textWidth - (2*sidePad) , (String)value));
        }
        if( !isVertCentered ) {
        label.setVerticalAlignment(JLabel.TOP);
        }
         */// </editor-fold>


        // Using the MultiLine Library from: http://samuelsjoberg.com/archive/2009/10/multiline-labels-in-swing
        label.setText((String) value);
        label.setFont(font);
        label.setBorder(BorderFactory.createEmptyBorder(sidePad, sidePad, sidePad, sidePad));
        
        if(isHorizCentered) {
          label.setHorizontalTextAlignment(JLabel.CENTER);
        }
        if( !isVertCentered ) {
          label.setVerticalTextAlignment(JLabel.TOP);
        }
        
        return label;
      }
    } //////////// end nested class ImageRenderer ///////////////

    ////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////
    /// NESTED CLASS ProConCellRenderer  ///////////////////
    /*
     *  http://pekalicious.com/blog/custom-jpanel-cell-with-jbuttons-in-jtable/
     */
    protected class ProConCellRenderer extends DefaultTableCellRenderer {

      JPanel panel = new JPanel();
      MultiLineLabel label;
      //JLabel label;

      // Constructor
      public ProConCellRenderer() {
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.setBackground(table.getBackground());
      }

      @Override
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                     boolean hasFocus, int row, int column) {

        // Call the super according to "camickr" on stackoverflow: http://stackoverflow.com/questions/3222951/java-put-image-in-jtable-cell
        super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        List <ProCon> proConList = (List <ProCon>)value;

        panel.removeAll();
        //panel.setPreferredSize(new Dimension(prosColWidth, row_height)); // trying to force the labels to wrap

        // Iterate through list of pros or cons and add it's main phrase to the panel (as a JLabel)
        for(ProCon proCon: proConList) {

          Box horizBox = Box.createHorizontalBox();
          horizBox.add(new JLabel("* ")); // bullet point
          //label = new MultiLineLabel("* " + proCon.getMainPhrase());
          label = new MultiLineLabel();
          label.setFont(plainFont);
          label.setText(proCon.getMainPhrase());
          // label.setText(String.format("<html><div style=\"width:%dpx;\">%s</div><html>", 35, proCon.getMainPhrase()));
          horizBox.setAlignmentX(Box.LEFT_ALIGNMENT);
          // label.setPreferredSize(new Dimension(35, 20)); // doesn't do anything
          // label.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15)); // works but doesn't force wrap or ellipses

          horizBox.add(label);
          panel.add(horizBox);
          //panel.add(label);
          panel.add(Box.createRigidArea(new Dimension(0, 12)));  // create some vertical padding b/t
        }
        panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        
        return panel;
      }
    } /////// end nested class ProConCellRenderer  /////////////

    /*
    public class ProConCellPanel extends JPanel {
      @Override
      public Dimension getPreferredSize() {
        return new Dimension(prosCol.getWidth(), row_height);
      }
    } /////////// end nested class ProConCellPanel //////////////
     */

    ////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////
    /// NESTED CLASS MyHeaderRenderer  ///////////////////
    /*
     *  http://www.exampledepot.com/egs/javax.swing.table/CustHeadRend.html
     */
    protected class MyHeaderRenderer extends DefaultTableCellRenderer {

      JPanel panel = new JPanel();
      JLabel label = new JLabel();
      Color bgColor = new Color(215,225,215);
      // Color fgColor = new Color(75,145,75);

      public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex) {

        label.setText(value.toString());
        label.setHorizontalTextPosition(JLabel.CENTER);
        label.setHorizontalAlignment(JLabel.CENTER);
        //label.setForeground(fgColor);

        label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        //label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
        //label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 1, new Color(85,85,85)));
        panel.setBackground(bgColor);

        panel.add(label);
        panel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 1, new Color(105,105,105)));
        return panel;
        //return label;
      }

    } /////////////////// end nested class MyHeaderRenderer //////////////////////////


    //////////////////////////////////////////////////
		/// NESTED CLASS MyTableModel //////////////////// Used to get data to the JTable, etc.
    class MyTableModel extends AbstractTableModel {

      private boolean DEBUG = false;
      private int columnCount = 6;

      private String[] columnNames = {"Name",
                                      "Main Pic",
                                      "Location",
                                      "Price",
                                      "Summary",
                                      "Pros",
                                      "Cons" };

        private Vector < Vector<Object> > data = new Vector < Vector<Object> >();
           //public ImageIcon imageIcon;

        // Nested class constructor
        public MyTableModel() {

          Vector <List_Item_Data> itemsVec = model.getItemsVec();
          List_Item_Data item;
          Vector <Object> rowVec;

          // Fill up the "data" 2D vec for the table to use to populate rows
          for(int row = 0; row < itemsVec.size(); row++) {

            rowVec = new Vector<Object>();
            item = itemsVec.elementAt(row);
            // Name
            rowVec.add(item.getName());
            nameColNum = 0;  // set which col this item is going into so outer class can set sizes, etc
            // MAIN PIC
            //rowVec.add(item.getMainPicImageIcon()); // Must use the ImageIcon version for JTable
                                                    //  and also override the cell renderer (nested class also in this file)
            
            BufferedImage mainPicResizedBufferedImage = Scalr.resize(item.getMainPicBufferedImage(),
                                                                     mainPicColWidth);

            // Must provide an ImageIcon for it to get rendered correctly in the JTable. -1 width/height mean don't scale it at all
            rowVec.add(new ImageIcon(mainPicResizedBufferedImage.getScaledInstance(-1, -1, 0)));

            mainPicColumnNum = nameColNum +1;
            // LOCATION
            BufferedImage locationPicResizedBufferedImage = Scalr.resize(item.getLocationPicBufferedImage(),
                                                                     locationPicColWidth);
            // Must provide an ImageIcon for it to get rendered correctly in the JTable. -1 width/height mean don't scale it at all
            rowVec.add(new ImageIcon(locationPicResizedBufferedImage.getScaledInstance(-1, -1, 0)));
            locationPicColNum = mainPicColumnNum +1;

            // PRICE
            String formatted_price = new String("$");
            formatted_price += format_price(item.getPrice());
            rowVec.add(formatted_price);
            priceColNum = locationPicColNum +1;
            // SUMMARY
            rowVec.add(item.getSummary());
            summaryColNum = priceColNum +1;
                  //lastColNum    = summaryColNum;
            // PROS
            rowVec.add(item.getPros());
            prosColNum = summaryColNum +1;
            // CONS
            rowVec.add(item.getCons());
            consColNum = prosColNum +1;

            data.add(rowVec);
          }

        } /////// end constructor

        public int getColumnCount() { return columnNames.length;      }
        public int getRowCount() {   /* return data.length; */    return data.size();   }
        @Override
        public String getColumnName(int col) {  return columnNames[col];      }
        public Object getValueAt(int row, int col) { /*return data[row][col]; */
          //if(data.elementAt(row).elementAt(col).getClass() == BufferedImage) {
          //}
          return data.elementAt(row).elementAt(col);
        }
        /*
         * JTable uses this method to determine the default renderer/editor for each cell.
         *  If we didn't implement this method, then the last column would contain
         *  text ("true"/"false"),rather than a check box.
         */
        @Override
        public Class getColumnClass(int c) {   return getValueAt(0, c).getClass();      }
        /*
         * Don't need to implement this method unless your table is editable.
         */
        @Override
        public boolean isCellEditable(int row, int col) {
            //Note that the data/cell address is constant, no matter where the cell appears onscreen.
            if (col < 2) { return false;  } 
            else {         return true;  }
        }
        /*
         * Don't need to implement this method unless your table's data can change.
         */
        @Override
        public void setValueAt(Object value, int row, int col) {
          if (DEBUG) {
              System.out.println("Setting value at " + row + "," + col + " to " + value
                               + " (an instance of " + value.getClass() + ")");
          }
          //data[row][col] = value;
          data.elementAt(row).set(col, value);
          fireTableCellUpdated(row, col);

          if (DEBUG) {
              System.out.println("New value of data:");
              printDebugData();
          }
        } ////// end setValueAt() ////////////


        // Add a new row to the table.  This method will be called when this view (Spreadsheet)
        // gets notified that a new list item has been added to the model.
        public void appendNewRow(List_Item_Data newItem) {

          Vector <Object> rowVec = new Vector<Object>();

          // NAME
          rowVec.add(newItem.getName());
          nameColNum = 0;  // set which col this newItem is going into so outer class can set sizes, etc
          // MAIN PIC         
          BufferedImage mainPicResizedBufferedImage = Scalr.resize(newItem.getMainPicBufferedImage(),
                                                                   mainPicColWidth);
          // Must provide an ImageIcon for it to get rendered correctly in the JTable. -1 width/height mean don't scale it at all
          rowVec.add(new ImageIcon(mainPicResizedBufferedImage.getScaledInstance(-1, -1, 0)));
          mainPicColumnNum = nameColNum +1;
          // LOCATION
          BufferedImage locationPicResizedBufferedImage = Scalr.resize(newItem.getLocationPicBufferedImage(),
                                                                   locationPicColWidth);
          // Must provide an ImageIcon for it to get rendered correctly in the JTable. -1 width/height mean don't scale it at all
          rowVec.add(new ImageIcon(locationPicResizedBufferedImage.getScaledInstance(-1, -1, 0)));
          locationPicColNum = mainPicColumnNum +1;
          // PRICE
          String formatted_price = new String("$");
          formatted_price += format_price(newItem.getPrice());
          rowVec.add(formatted_price);
          priceColNum = locationPicColNum +1;
          // SUMMARY
          rowVec.add(newItem.getSummary());
          summaryColNum = priceColNum +1;
                //lastColNum    = summaryColNum;
          // PROS
          rowVec.add(newItem.getPros());
          prosColNum = summaryColNum +1;
          // CONS
          rowVec.add(newItem.getCons());
          consColNum = prosColNum +1;
          // append the row to the data vec
          data.add(rowVec);


          // Notify all listeners that rows in the range [firstRow, lastRow], inclusive, have been inserted.
          int firstRow = model.getItemsVec().size();
          int lastRow = firstRow;
          fireTableRowsInserted(firstRow, lastRow); // this should append this new item to the table

        } /////////// end appendNewRow() //////

        private void printDebugData() {
          int numRows = getRowCount();
          int numCols = getColumnCount();

          for (int i=0; i < numRows; i++) {
              System.out.print("    row " + i + ":");
              for (int j=0; j < numCols; j++) {
                  System.out.print("  " + /* data[i][j] */ data.elementAt(i).elementAt(j));
              }
              System.out.println();
          }
          System.out.println("--------------------------");
        } // end printDebugData() /////


        // <editor-fold defaultstate="collapsed" desc="format_price()">
        // Returns a formatted price (with commas) for better readability.
        protected String format_price(int price) {

          String price_unformatted = new String(Integer.toString(price));
          String price_formatted = new String();
          int position = 0;
          int unformatted_length = price_unformatted.length();
          ///////////////////////////////////////////////////////////////////////////////
          ////// 3 Cases: either 0,1, or 2 beginning digits before the first comma //////

          // First take the first chunk of digits before transferring all other chunks (3 cases for this)
          switch (price_unformatted.length() % 3) {

            case 0:
              price_formatted += price_unformatted.substring(0, 3); // grab first 3
              position = 3;
              price_unformatted = price_unformatted.substring(position, price_unformatted.length()); // chop off 3
              break;
            case 1:
              price_formatted += price_unformatted.substring(0, 1); // grab first 1
              position = 1;
              price_unformatted = price_unformatted.substring(position, price_unformatted.length()); // chop off 3
              break;
            case 2:
              price_formatted += price_unformatted.substring(0, 2); // grab first 2
              position = 2;
              price_unformatted = price_unformatted.substring(position, price_unformatted.length()); // chop off 3
              break;
            default:
              System.err.println("Bad code in switch statement in format_price()!  Ending prog now.");
              System.exit(-1);
          }

          // For the rest of the string, add commas 3 chars at a time until the whole string has been added
          while (position < unformatted_length) {
            price_formatted += ",";
            price_formatted += price_unformatted.substring(0, 3); // grab 3
            position += 3;
            price_unformatted = price_unformatted.substring(3, price_unformatted.length()); // chop off 3
          }

          return price_formatted;

        } ////// end format_price() ///////////// </editor-fold>
 
    } /////// end nested class MyTableModel/////////////
    ////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////
    /// NESTED CLASS MyScrollPane       ///////////////////
    /*
     *  Using so I can base the scrollpane's width on the current width of the columns in the table
     */
    protected class MyScrollPane extends JScrollPane {

      // Constructor
      public MyScrollPane(JTable table) {    super(table);    }

      protected int getAllColsWidth() {
        int width = 4;  // need a few pixels extra to ensure no horiz scrollbar appears
        int approxScrollbarWidth = 15; // Add some width for screens that immediately require
                                        // a vert scrollbar to avoid the horiz scrollbar @ beg
        
        width +=  approxScrollbarWidth; // TODO: remove this later (its for formatting to the presentation laptop)



        for(TableColumn tableCol: tableColsVec) {
          width += tableCol.getWidth();
        }

        return width;
      }

      @Override
      public Dimension getPreferredSize() {  return new Dimension(getAllColsWidth(), 
                                                                  myScrollPane_PrefHeight);
                                          }
      
      public Dimension getMaximumSize() {    return new Dimension(getAllColsWidth(), 
                                                                  myScrollPane_MaxHeight);   }
      
    } /////// end nested class MyScrollPane /////////////
    /////////////////////////////////////////////////////


  @Override
  public void paintComponent(Graphics g) {
    // Call the superclass paintComponent() to clear the clipping region (avoids ghosting).
    super.paintComponent(g);
    /*
    // make a deep copy of the Graphics object before modifying it
    Graphics myG = g.create();
    Graphics2D g2 = (Graphics2D) myG;
    setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));


     System.out.println("At the end of paintComponent in SpreadsheetView.");
    // clear up resources
    myG.dispose();
    */
  } ///// end paintComponent() ///


  /////////////////////////////////////////////////
  ////// Methods Required by the Interface ////////

  // the Model calls this to notify this View of a changed current value
  public void curViewChanged(String old_view_name, String new_view_name) {
    if( old_view_name.equals(this.view_name) ) {

    }
    if( new_view_name.equals(this.view_name)) {

      System.err.println("SpreadsheetView noticed that it is the new view!");
    }
    // System.err.println("SpreadsheetView's curViewChanged() called.");
  } ////// end curViewChanged() /////////

  // Model calls this to inform views of an added (new) list item.
  //  The views will then probably need to re-layout their list items to reflect the change.
  public void itemAdded(List_Item_Data newItem) {

    myTableModel.appendNewRow(newItem);
  }
  // Model calls this to inform views of a changed list item.
  //  The views will then probably need to re-layout their list items to reflect the change.
  public void itemChanged() {
    
    System.out.println("\nSpreadsheetView notices that a list item was changed.\n");
  } //////// end itemChanged() ////////////////

  public String getViewName() {
    return this.view_name;
  } ////

  /////// end required by interface functions  //////
  ///////////////////////////////////////////////////

/*
  @Override
  public Dimension getMinimumSize()   { return getPreferredSize(); }
  @Override
  public Dimension getPreferredSize() { return new Dimension(panel_width, panel_height); }
*/
} //////////////////// end class SpreadsheetView /////////////////////////////
